﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ObviousCode.Stately.Compiler.ContextMenuHandler.Values;
using StatelyCompiler;
using System.IO;
using ObviousCode.Stately.Compiler.Parser;
using ObviousCode.Stately.Compiler.Templating;

namespace ObviousCode.Stately.Compiler.ContextMenuHandler
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Strings.Title);
            Console.WriteLine("{0}, {1}".F(Strings.Company, Strings.Year)); ;
            Console.WriteLine();

            Console.WriteLine("Transforming {0} ...".F(args[0]));

            Console.WriteLine("Searching for compiler values ...");

            ValuesContext context = new ValuesContext();

            context.MapFilename = args[0];
            context.Callback = (c) =>
                {
                    TransformerContext transformContext = new TransformerContext();

                    transformContext.MapFileName = args[0];
                    transformContext.ValuesContext = c;

                    RunLexer(transformContext);
                };

            ValuesReader reader = new ValuesReader();

            reader.Start(context, context.Callback);

            reader.FileNotFound += (s, e) => { 
                Console.WriteLine("Unable to locate {0}".F(args[0])); 
                OutTransformFailed(); 
            };
            reader.MapFilenameNotSet += (s, e) => Console.WriteLine("Filename not set.");
            reader.ExceptionOccured += (s, e) => Console.WriteLine(e.Item.Message);

            //Console.ReadLine();
        }

        private static void OutTransformFailed()
        {
            Console.WriteLine();
            Console.WriteLine("Transform Failed");
        }

        private static void RunLexer(TransformerContext context)
        {
            Console.WriteLine("Running Lexer ...");

            Lexer lexer = new Lexer();

            try
            {
                using (FileStream stream = File.OpenRead(context.MapFileName))
                {
                    context.Tokens = lexer.Lex(stream);

                    Parse(context);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                OutTransformFailed();
            }
        }

        private static void Parse(TransformerContext context)
        {
            Console.WriteLine("Parsing ...");

            Parser.Parser parser = new Parser.Parser();

            parser.ExceptionOccured += (s, e) =>
                {
                    Console.WriteLine(e.Item.Message);
                    Console.WriteLine(e.Item.StackTrace);
                    OutTransformFailed();
                };


            parser.Parse(context.Tokens, t =>
                {
                    context.TransformData = t;

                    CreateTextFiles(context);
                });
        }

        private static void CreateTextFiles(TransformerContext context)
        {
            Console.WriteLine("Creating Files ...");
            context.UpdateTransformDataWithClassDetails();

            Console.WriteLine("Class Name = {0}", context.TransformData.ClassName);

            if (!GeneratePartialClass(context)) return;
            if (!GenerateStubClass(context)) return;

            if (context.TransformData.UseDataContext)
            {
                if (!GenerateDataContext(context)) return;
            }
            else
            {
                Console.WriteLine("Data context skipped ...");
            }

            Console.WriteLine("Complete");
            Console.WriteLine("Press Enter to Close");
            Console.ReadLine();
        }
        
        private static bool GeneratePartialClass(TransformerContext context)
        {
            if (File.Exists("__{0}.cs".F(context.TransformData.ClassName)))
            {
                Console.WriteLine("Deleting old __{0}.cs".F(context.TransformData.ClassName));

                try
                {
                    File.Delete("__{0}.cs".F(context.TransformData.ClassName));
                }
                catch (Exception e)
                {
                    Console.WriteLine("Can't delete: {0}".F(e.Message));
                }

                Console.WriteLine("Deleted ...");
            }

            Console.WriteLine("Writing __{0}.cs".F(context.TransformData.ClassName));
            try
            {
                PartialClassTemplate partialClass = new PartialClassTemplate(context.TransformData);

                File.WriteAllText(context.PartialClassFileFullName, partialClass.TransformText());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                OutTransformFailed();
                return false;
            }

            return true;
        }

        private static bool GenerateStubClass(TransformerContext context)
        {
            Console.WriteLine("Writing {0}_stub.cs".F(context.TransformData.ClassName));
            try
            {
                StubClassTemplate stubClass = new StubClassTemplate(context.TransformData);

                File.WriteAllText(context.StubClassFileFullName, stubClass.TransformText());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);

                OutTransformFailed();
                return false;
            }

            return true;
        }

        private static bool GenerateDataContext(TransformerContext context)
        {
            Console.WriteLine("Writing {0}_stub.cs".F(context.TransformData.DataContextName));
            try
            {
                DataContextTemplate contextClass = new DataContextTemplate(context.TransformData);

                File.WriteAllText(context.DataContextFileFullName, contextClass.TransformText());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                OutTransformFailed();
                return false;
            }

            return true;
        }        


        internal class TransformerContext
        {

            public ValuesContext ValuesContext { get; set; }

            public string MapFileName { get; set; }

            public List<Token> Tokens { get; set; }

            public Parser.TransformData TransformData { get; set; }

            internal void UpdateTransformDataWithClassDetails()
            {
                UpdateTransformDataClassName();

                UpdateTransformDataNamespace();

                if (!string.IsNullOrWhiteSpace(ValuesContext.DataContext))
                {
                    TransformData.DataContextName = ValuesContext.DataContext;
                }
            }

            private void UpdateTransformDataNamespace()
            {
                if (string.IsNullOrWhiteSpace(ValuesContext.Namespace))
                {
                    TransformData.Namespace = "MyNamespace";
                }
                else
                {
                    TransformData.Namespace = ValuesContext.Namespace;
                }
            }

            private void UpdateTransformDataClassName()
            {
                if (string.IsNullOrWhiteSpace(ValuesContext.Classname))
                {
                    FileInfo file = new FileInfo(MapFileName);

                    TransformData.ClassName = file.Name.Left(file.Name.Length - file.Extension.Length);
                }
                else
                {
                    TransformData.ClassName = ValuesContext.Classname;
                }
            }

            public string PartialClassFileFullName
            {
                get
                {
                    FileInfo file = new FileInfo(MapFileName);

                    return Path.Combine(file.DirectoryName, "__{0}.cs".F(TransformData.ClassName));
                }
            }

            public string StubClassFileFullName
            {
                get
                {
                    FileInfo file = new FileInfo(MapFileName);

                    return Path.Combine(file.DirectoryName, "{0}_stub.cs".F(TransformData.ClassName));
                }
            }

            public string DataContextFileFullName
            {
                get
                {
                    FileInfo file = new FileInfo(MapFileName);

                    return Path.Combine(file.DirectoryName, "{0}_stub.cs".F(TransformData.DataContextName));
                }
            }
        }

    }
}
